package rpcserver

import (
	"context"
	"fmt"
	v1 "gluenet/pkg/log/v1"
	"sync"
	"time"

	"github.com/wutianze/nats.go"
)

var logger = v1.NewLogger("rpcserver")

type RpcFunc func(c *Context)

type RpcHandler struct {
	mu sync.RWMutex
	m  map[string]RpcFunc
}

func NewRpcHandler() *RpcHandler {
	return new(RpcHandler)
}

func (r *RpcHandler) HandleFunc(pattern string, handler RpcFunc) {
	r.mu.Lock()
	defer r.mu.Unlock()

	if pattern == "" {
		panic("rpc: invalid pattern")
	}
	if handler == nil {
		panic("rpc: nil handler")
	}
	if _, exist := r.m[pattern]; exist {
		panic("rpc: multiple registrations for " + pattern)
	}
	if r.m == nil {
		r.m = make(map[string]RpcFunc)
	}

	r.m[pattern] = handler
}

type RpcServer struct {
	Guid       string
	RpcConfig  string
	RpcHandler *RpcHandler
	nc         *nats.Conn
}

func (s *RpcServer) ListenAndServe() error {
	nc, err := nats.IConnect(s.RpcConfig)
	if err != nil {
		return err
	}

	logger.Infof("listen rpc at: %s", s.Guid)

	for pattern, handler := range s.RpcHandler.m {
		handlerRegister(nc, s.Guid, pattern, handler)
		logger.Debug(pattern)
	}

	s.nc = nc
	return nil
}

func newNatContext(pattern string, data []byte) *Context {
	return &Context{
		Request: &Request{
			Pattern: pattern,
			Data:    data,
		},
		Response: new(Response),
	}
}

func handlerRegister(nc *nats.Conn, guid, pattern string, handler RpcFunc) {
	route := fmt.Sprint(guid, `:`, pattern)

	nc.ISubscribe(route, func(m *nats.Msg) {
		c := newNatContext(pattern, m.Data)
		start := time.Now()

		handler(c)

		total := time.Since(start)
		if err := m.IRespond(c.Response.Data); err != nil {
			logger.Errorf("api: %s -> err: %s", pattern, err.Error())
		}

		logger.Infof("api: %s | time: %v", pattern, toUs(total))
	})
}

func (s *RpcServer) Close() error {
	s.nc.Close()
	return nil
}

func (s *RpcServer) Shutdown(ctx context.Context) error {
	s.nc.Close()
	return nil
}
